perm filename INGGP.MAC[IP,SYS]1 blob sn#680207 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-INET>INGGP.MAC.40301 29-Jan-82 15:12:05, Edit by CLYNN
; Have GGPINI enter section 1 for MDDT calls
; Updated for IP release 3
; Add global routine FNDPGW to find a prime gateway
; Moved parameters & variables to STG, increased gateway block size
;[BBNF]<402-INET>INGGP.MAC.116  21-Oct-81 17:43;00, Edit by CLYNN
; Fix GGP Echo packet freeing problem
;[BBNF]<402-INET>INGGP.MAC.115,  3-Aug-81 11:00:00, Ed: CLYNN
; Fix: Wrong register at .GGPEC+5; Process all gateways in .GGPER
;[BBNF]<402-INET>INGGP.MAC.114, 13-Jul-81 10:55:00, Ed: CLYNN
; Fix: Save T2 from FINDGW & correct GW%DUM testin .GGPDU
;[BBND]<402-INET>INGGP.MAC.112, 10-Feb-81 09:46:40, Ed: CLYNN
;Increase ping time from 17 to 37 seconds
;		GGPPT0==↑D<17*1000>	GGPPT0==↑D<37*1000>
;Missing out-of-core error return checks:
;	GGPINI+6	CALL GETBLK		CALL GETBLK
;						JUMPE T1,GGPIN9
;
;	GGPI4A+1	CALL GETBLK		CALL GETBLK
;						JUMPE T1,GGPIN9
;[BBND]SNARK:<402-INET>INGGP.MAC.111, 27-Jan-81 15:44:15, Ed: TAPPAN
;Only a single packet is processed per call, not all received
;	GGPDSP+2	MOVE T1,GGPIPQ	GGPDS1:	MOVE T1,GGPIPQ
;
;	GGPDS9:		CALL RETPKT		CALL RETPKT
;						JRST GGPDS1

	SEARCH	INPAR,PROLOG
	TTITLE	INGGP
	SUBTTL	Internet Gateway-Gateway Protocol, Wm. W. Plummer, May79
	SWAPCD
COMMENT	!
	These routines are concerned with keeping this host gateway
	in contact with other gateways.  Only enough of the GGP is
	implemented so that we can operate with out burdening our CPU.

            3-6 ...... Definitions
* GGPINI ...  7 ...... Initialize G-G Protocol
  LODFIL ...  9 ...... Load the gateway file
* PRCLIN ... 10 ...... Process lines from the gateway file
  LOADGW ... 13 ...... Load one gateway description and add to table
  GETC   ... 17 ...... Get a character from a file
  SNDGWD ... 18 ...... Send our gateway file date to all
  SNDGFT ... 19 ...... Send gateway file date and source to a gateway
  SETROU ... 21 ...... Set routing tables from GW status tables
  RSTDSP ... 22 ...... Reset gateway dispatches which are not now set
* GGPPRC ... 23 ...... Top level GGP Processing routine
* GGPCHK ... 23 ...... GGP next run time check routine
  PINGER ... 24 ...... Ping gateways to see if they are alive
  SNDPNG ... 26 ...... Send a ping message to a gateway
  GWDOWN ... 28 ...... Just detected gsteway down
  GGPDSP ... 29 ...... Dispatch on GGP message type
             31 ...... Gateway message type dispatch tables
  .GGPEC ... 32 ...... ECHO message
  .GGPER ... 32 ...... ECHO-REPLY message
  ..GPER ... 32 ...... Work routine for FNDAGW
  .GGPRD ... 33 ...... REDIRECT message
  .GGPDU ... 34 ...... DESTINATION-UNREACHABLE message
  ..GPDU ... 34 ...... Work routine for FNDGAW
  .GGPRU ... 35 ...... ROUTING-UPDATE message (%FULL only)
  .GGPHD ... 36 ...... HOST-DISTANCE message (%FULL only)
  .GGPFD ... 37 ...... FILE-DATE message
  ..GPFD ... 38 ...... Routine to print out new file message
  FINDGW ... 39 ...... Set GW to point to gateway block with address in T1
  FNDAGW ... 40 ...... For all matching gateway blocks, call a subroutine
* FNDPGW ... 41 ...... Find a PRIME gateway that we can communicate with
  FNDGWP ...    ...... Find gateway address & get a packet ready for it
  GGPIPK ...    ...... Get & Initialize a GGP packet
  PRNIH  ... 42 ...... Print Internet host number string
  GGPCKS ... 43 ...... Compute GGP level checksum (0 for now)
  GGPACK ... 44 ...... Unimplemented
  SETHGD ... 44 ...... Unimplemented
  .GGPAK ... 44 ...... Unimplemented
  .GGPSQ ... 44 ...... SOURCE-QUENCH message Unimplemented
  .GGPAK ... 44 ...... ACKNOLEDGEMENT message Unimplemented
  .GGPNK ... 44 ...... NEGATIVE-ACKNOLEDGEMENT message Unimplemented
  .GGPNS ... 44 ...... Unimplemented
  .GGPHD ... 44 ...... HOST-DISTANCE dummy (not %FULL)
  .GGPRU ... 44 ...... ROUTING-UPDATE dummy (not %FULL)

	!

IFNDEF %FULL,<	PRINTX % %FULL not defined, assumed 0
%FULL==0			; Turn on to be an active IP
>

; Accumulators used globally in this module:

DEFAC(GW,BFR)			; Points to a gateway block
DEFAC(GPKT,TPKT)		; Index register to point to GGP pkt


; Parameters:

IFNDEF MAXGWA,<MAXGWA==20>	; Number of GWs we will keep track of
IFNDEF MAXROU,<MAXROU==40>	; Number of routing names
				; (Gateways and multi-homed hosts)

;	Defined in STG as a variable
;GGPPT0==↑D<37*1000>		; Milliseconds of inter-ping interval
				; (Prime number of seconds to minimize
				;  correlation with other things)
				; (Not too often or there will be
				;  problems sending do down ARPANET hosts!)

DAY0==126317000000		; Day-0 for GGP%FD pkts (1Jan80 0000-GMT)



; The file name to use:

IFKA <	GWFILE:	ASCIZ "<SYSTEM>INTERNET.GATEWAYS">
IFNKA <	GWFILE:	ASCIZ "SYSTEM:INTERNET.GATEWAYS">

; Structure of GGP packet (pointed to by GPKT)

DEFSTR(GPTYP,0,7,8)		; Type of message
  GGP%RD==5			; Redirect your output
  GGP%DU==3			; Destination unreachable
  GGP%SQ==4			; Source quench
  GGP%RU==1			; (old) Routine update
  GGP%HD==101			; (new) Host distance
    .GGPSO==1			; Where substructure begins
  GGP%FD==102			; (new) File date
  GGP%AK==2			; Acknowledgement
  GGP%NK==↑D10			; Negative acknowledgement
  GGP%EC==↑D8			; Echo
  GGP%ER==0			; Echo reply
  GGP%NS==↑D9			; Network Interface Status
DEFSTR(GPCOD,0,15,8)		; Code for GGP%DU
  GGP%NU==0			; Network unreachable
  GGP%HU==1			; Host unreachable
DEFSTR(GPSEQ,0,31,16)		; Sequence number in GGP%RU, GGP%AK,
				; GGP%NK, and GGP%HD
DEFSTR(GPCKS,0,0,0)		; Checksum field (****Not Defined Yet)


; Other entries depend on GPTYP

; (Old) Routine update:		GGP%RU
DEFSTR(GPNU,1,0,1)		; Source GW request update from Dest GW
DEFSTR(GPCNT,1,7,7)		; Count for GGP%RU
DEFSTR(GPDS1,1,15,8)		; Distance 1, followed by one octet per net

; Destination unreachable:	GGP%DU
; Source quench:		GGP%SQ
GIP%DU==<GIP%SQ==1>		; Beginning of included IP header

; Redirect your output:		GGP%RD
DEFSTR(GPGWA,1,31,32)		; Gateway address for GGP%RD msgs
GIP%RD==2			; Beginning of included IP header

; (New) Host distance:		GGP%HD
; (New) File date:		GGP%FD
DEFSTR(GPSRC,1,31,32)		; Source of Gateway data file in GGP%FD
DEFSTR(GPTAD,2,31,32)		; File time and date in GGP%FD

; Substructure of a Host Group Distance message:

DEFSTR(HGHST,0,31,32)		; 32-bit host (net, ...) identifier
DEFSTR(HGDST,1,31,16)		; Distance to that host group
  HGSIZE==2			; Words per subblock

; Note that GGP%RD, GGP%SQ, and GGP%RU have
; an Internet header and 64 bits in their bodies.


GGPNPW==PKTELI+.RTJST(-1,PIDO)+1 ; Minimum GGP packet size (1 data word)

; Tables kept and used by INGGP and INGWAY:


;GWTAB(GWX) is a list of pointers to gateway blocks that we know about.

;GWTAB			; Pointer to the table

;0,,GWTAB/	INTSEC,,Adr -->	INTSEC,,Adr -->	Gateway block
;				...
;				INTSEC,,Adr -->	Gateway block
;				...
;				0
;			(MAXGWA)



; Structure of a Gateway block:

DEFSTR(GWUP,0,0,1)		; Gateway should be useable
DEFSTR(GWPIP,0,1,1)		; Ping in progress
DEFSTR(GWTYP,0,5,4)		; Gateway type
  GW%PRM==1			; Prime: Speaks GGP
  GW%DUM==2			; Dumb: Fwd's pkts, but that's all
  GW%HST==3			; Host: Avoid except in emergency
  GW%AUP==4			; Always-up: Fwds but doesn't reflect
DEFSTR(GWHIS,0,14,7)		; Ping history bits
DEFSTR(GWSPC,0,17,3)		; Successful ping count
  .THRUP==<6*WID(GWHIS)>/8	; Threshold to say it is up
  .THRDN==<3*WID(GWHIS)>/8	; Threshold to say it is down
  IFL <<1←WID(GWSPC)>-1>-WID(GWHIS), PRINTX ?GWSPC too small
DEFSTR(GWICT,0,35,18)		; Interface count
.GWILS==1			; Where in block to find the list
MXGWIC==6			; Maximum number of interfaces
		; Note: Really max number of nets can get to
		;	through a gateway plus 1
GWBKSZ==.GWILS+MXGWIC		; Size of a gateway block



;0,,NETGWA+I/	INTSEC,,Adr -->
;		...
;		INTSEC,,Adr -->	+0/	-1
;		-1			...
;		...		+NET/	Address of GW to get to NET
;		-1			...
;	($NETS)			(MAXNET)


;0,,LCLHID+0/	-1
;		...
;	  +NET/	Interface #,Our interface address on NET
;		...
;		-1
;	(MAXNET)



IFN %FULL,<
; ROUTE(hash(NET)) gives the current routine information for
; packets going to a given "network".  Note that a network number
; is a 32-bit number which is the name of a host group.  For
; instance 12,3,0,61 is BBNC; 12,0,0,0 is the ARPANET.

DEFSTR(RUDEL,0,0,1)		; Deleted slot in the hash table
DEFSTR(RUGWX,0,17,6)		; Gateway index
DEFSTR(RUGIN,0,35,18)		; Interface on that GW
DEFSTR(RUNET,1,35,36)		; The lookup key

RUBKSZ==2			; Size of routing blocks

;ROUTAB				; Pointer to table
;LSTROU				; Pointer to stale routing table
> ; end IFN %FULL

;GGPINI		Initialize G-G Protocol

;
;	CALL GGPINI
;Ret+1:	Always.

GGPINI::SE1CAL			; In case called from MDDT
	LOCAL <I>
	PUSH P,GW
	MOVX T1,<377777777777>	; No run time
	MOVEM T1,GGPTIM
	MOVX T1,.GGPFM		; Set protocol number
	MOVEM T1,GGPPCL
	SETOM GGPMSG		; Use .PRIOU for messages

	SETZM GGPPTM		; Clear time of next ping

	SKIPE GGPIPQ		; Already have a queue head?
	  JRST GGPIN0		; Yes.
	MOVEI T1,QSZ		; Size of a queue head
	CALL GETBLK		; Get one from free area
	JUMPE T1,GGPIN9		; No core
	MOVEM T1,GGPIPQ		; Put where we can find it
	CALL INITQ		; Initialize it
GGPIN0:
	SKIPN GWTAB		; Is this a reinit?
	 JRST GGPIN2		; No.

	MOVSI I,-MAXGWA		; Set to scan GWTAB
GGPIN1:	MOVE GW,GWTAB
	ADDI GW,0(I)		; Point to actual entry
	SETZ T1,		; Get a zero
	EXCH T1,0(GW)		; Flush entry, get previous value
	SKIPE T1		; Was there one?
	 CALL RETBLK		; Yes.  Give back storage
	AOBJN I,GGPIN1		; Do all GW blocks
	JRST GGPIN4

GGPIN2:	SETZM GGPSID		; Init the segment ID for GGP
	MOVEI T1,MAXGWA		; Maximum number of gateways
	CALL GETBLK		; Get a block of storage
	JUMPE T1,GGPIN9		; Crash
	MOVEM T1,GWTAB
	MOVEI T2,MAXGWA		; Size of the block
	CALL CLRBLK		; Clear it out
IFN %FULL,<
	MOVEI T1,MAXROU*RUBKSZ	; Size of a routine table
	CALL GETBLK
	JUMPE T1,GGPIN9
	MOVEM T1,LSTROU		; Last thing we announced
	MOVEI T1,MAXROU*RUBKSZ
	CALL GETBLK
	JUMPE T1,GGPIN9		; No core
	MOVEM T1,ROUTAB		; Here is the main routing table
> ; end IFN %FULL

	HRLZ I,$NETS		; Set to scan NETGWA
GGPI4A:	MOVEI T1,MAXNET		; Size of a gateway table
	CALL GETBLK		; Get one
	JUMPE T1,GGPIN9		; No core
	MOVEM T1,NETGWA(I)	; Set pointer in interface table
	AOBJN I,GGPI4A
	CALL SETROU		; Set up initial routes (none?)

GGPIN4:
IFN %FULL,<
	MOVE T1,LSTROU		; Pointer to old table
	MOVEI T2,MAXROU*RUBKSZ	; Size of it
	CALL CLRBLK		; Clear it
	MOVE T1,ROUTAB		; Pointer to main table
	MOVEI T2,MAXROU*RUBKSZ
	CALL CLRBLK
> ; end IFN %FULL

GGPIN5:	CALL LODFIL		; Load the gateway file
	CALL SETROU		; Set routes based on file
	CALL PINGER		; Ping the gateways
	MOVE T1,INETID+0	; Our (primary) Internet name
	MOVEM T1,GFSRC		; We are a source of gateway file
	MOVEM T1,GFWHO		; And we are the one who said so
	MOVE T2,GFCTAD		; Gateway file creation time and date
	MOVEM T2,GFTAD		; TAD of file anywhere in catenet
	CALL SNDGWD		; Send out this date

	JRST GGPINX

GGPIN9:	INBUG (HLT,<GGPINI: Crucial storage missing>,INGGP0)
GGPINX:	SETOM GGPON
	POP P,GW
	RESTORE
	RET

; LODFIL()	Load the gateway file

;
;	CALL LODFIL
;Ret+1:	Always

LODFIL:	LOCAL <JFH,CHNS>
	SETO JFH,		; Indicate nothing to release
	MOVEI T1,.FHSLF		; This fork
	RCM			; Get channels which are on
	MOVEM T1,CHNS		; Save for restoring
	MOVEI T1,.FHSLF
	MOVX T2,1B<.ICEOF>	; End of file channel
	DIC			; Prevent unwanted interrupt
	MOVX T1,GJ%OLD+GJ%SHT	; Want existing file
	HRROI T2,GWFILE		; Pointer to filename string
	GTJFN
	 JRST LODFIX		; Not there
	MOVEM T1,JFH
	MOVE T2,[FLD(7,OF%BSZ)+OF%RD]	; Want to read it
	OPENF
	 JRST LODFIX
	CALL PRCLIN		; Process lines in the file
	MOVE T1,JFH
	CLOSF
	 JFCL
LODFIX:	SKIPL T1,JFH
	 RLJFN
	  JFCL
	MOVEI T1,.FHSLF
	MOVE T2,CHNS
	AIC
	RESTORE
	RET

; PRCLIN(JFH)	Process lines of the gateway file

;T1/	JFH of the file
;
;	CALL PRCLIN
;Ret+1:	Always.  T1 still has the JFH

PRCLIN::LOCAL <JFH,BOL,ERRPNT,ERRCOL>
	MOVEM T1,JFH		; Stash JFH in a save place

; Top of main per-line loop:

PRCLI1:	MOVE T1,JFH		; Get the file JFH
	RFPTR			; Find out where in file line is
	 JFCL
	MOVEM T2,BOL		; Save beginning of line

	CALL GETC		; First character of line
	JUMPE T2,PRCLIX		; get out if end of file
	CAIN T2,12		; Linefeed?
	 JRST PRCLI1		; Ignore blank lines
	CAIN T2,";"
	 JRST PRCLI8		; Flush comment line
	CAIN T2,"C"
	 JRST PRCLI7		; Go do CREATION command
	BKJFN			; Back up so LOADGW can read 1st chr
	 JFCL			; Will ITRAP on BIN if error in T1
	CALL LOADGW		; Load a gateway description
	JUMPE T2,PRCLI1		; Do next if no error

; Here when error detected in current line	***** Noone might be
						; listening, Hang fork??
PRCLI2:	MOVE T1,GGPMSG
	SETZ T3,
	SOUT			; Type the error string
	HRROI T2,[ASCIZ " in file: "]
	SOUT
	MOVE T2,JFH
	JFNS			; And the actual file name
	HRROI T2,[ASCIZ "
"]
	SOUT			; And a carriage return

	MOVE T1,JFH
	RFPTR			; Find out where we have read to
	 JFCL
	SOS ERRPNT,T2		; Save the error point
	MOVE T2,BOL		; Beginning of the bad line
	SFPTR
	 JFCL
	SETOM ERRCOL		; Maybe nothing read of line

; Top of loop that types out a bad line

PRCLI3:	MOVE T1,JFH
	RFPTR			; Get the file pointer
	 JFCL
	CAME T2,ERRPNT		; Up to the point of the error
	 JRST PRCLI4		; No.  Dont save column yet
	MOVE T1,GGPMSG
	RFPOS
	HRRZM T2,ERRCOL		; Column where to show error
PRCLI4:	MOVE T1,JFH
	CALL GETC		; Get a character from bad line
	SKIPN T2		; End of file?
	 MOVEI T2,12		; Yes.  Use linefeed.
	CAIN T2,12		; End of line?
	 JRST PRCLI5		; Yes. Done
	MOVE T1,GGPMSG
	BOUT			; Type a character
	JRST PRCLI3		; Do next one

PRCLI5:	MOVE T1,GGPMSG
	HRROI T2,[ASCIZ "
"]
	SETZ T3,
	SOUT			; Type and end of line terminal
	JUMPLE ERRCOL,PRCLI6	; Know where to show the error?
	MOVEI T2," "		; Yes.  Space over to it.
	BOUT
	SOJG ERRCOL,.-1		; All the way.
PRCLI6:	HRROI T2,[ASCIZ "↑
"]
	SOUT
	JRST PRCLI1		; Try to finish the file

; Do CREATION command

PRCLI7:	CALL GETC		; Skip over stuff following the C
	MOVE T3,T2		; Free up T2
	HRROI T2,[ASCIZ "% INGGP: Premature EOF"]
	JUMPE T3,PRCLI2		; Go do the error if need be
	CAIE T3," "		; One space is required separator
	 JRST PRCLI7		; Loop til it is found
	SETZ T2,		; Default flags
	IDTIM			; Input the time and date
	 SKIPA T2,[-1,,[ASCIZ "% INGGP: Bad format in creation date"]]
	 MOVEM T2,GFCTAD	; Save our gateway file creation date
	JUMPL T2,PRCLI2		; Do error if need be
	JRST PRCLI1		; Do another command

; Here to flush a comment line

PRCLI8:	CALL GETC		; Get a character
	JUMPE T2,PRCLIX		; Get out if end of file
	CAIE T2,12		; End of line?
	 JRST PRCLI8		; No.
	JRST PRCLI1		; Go read the next line.

PRCLIX:	MOVE T1,JFH		; Preserve JFH as promised
	RESTORE
	RET

;NR GFCTAD,1			; Gateway file creation time and date

; LOADGW(JFH)		Load one gateway desciption and add to table

;T1/	JFH
;
;	CALL LOADGW
;Ret+1:	Always. T2 has 0 if no error or -1,,errorstring
;		T1 preserved.

LOADGW:	LOCAL <JFH,EOLFLG>
	PUSH P,GW
	MOVEM T1,JFH
	MOVEI GW,0

	MOVEI T1,GWBKSZ		; Size of a gateway block
	CALL GETBLK		; Get a chunk of free storage
	HRROI T2,[ASCIZ "% INGGP: Insufficient storage "]
	SKIPN GW,T1
	 JRST LOADG9		; Give error return
	MOVEI T2,GWBKSZ		; Size of block
	CALL CLRBLK		; Clear it out
	SETZM EOLFLG		; End of line not seen

; Top of per-keyword loop:

LOADG1:	MOVE T1,JFH
	CALL GETC		; Get a character
	JUMPE T2,LOADG8		; Oops.  End of file.
	CAIN T2," "		; Space (control, etc)
	 JRST LOADG1		; Yes.  Flush it.
	CAIL T2,"0"
	CAILE T2,"9"
	 JRST LOADG4		; Non-digit.  Must be keyword

; Here to input an interface address in  N H L I form.

LOADG2:	SETZM T4		; Clear the number accumulator
	BKJFN			; Reread the digit
	 JFCL
LODG2A:	MOVEI T3,↑D10		; Decimal
	NIN
	 JRST LOADG7		; Null number?
	LSH T4,↑D8		; Make room for another byte
	ADD T4,T2		; Add it in
	BKJFN			; Reread the terminator
	 JFCL
	BIN
	CAIN T2,15		; Happens on TENEX
	 BIN			; Get the line feed, like TOPS20
	JUMPE T2,LOADG8		; Jump if end of file encountered
	CAIN T2," "		; Space means another byte follows
	 JRST LODG2A		; Go get it
	CAIN T2,12		; End of line?
	 SETOM EOLFLG		; Yes.  Remember to exit later.
	CAIE T2,12		; End of line
	CAIN T2,","		; End of address expression?
	 JRST LOADG3		; Yes.  Go enter into GW block
	JRST LOADG7		; Anything else is bad format.

; Put address in current GW block.  Really all that is needed is
; those addresses that we can talk to directly, but having the
; other sides of the gateways allows the first level of routing to
; be set up.

LOADG3:	LOAD T3,GWICT,(GW)	; Get current count
	CAIL T3,MXGWIC		; Room for another?
	 JRST LOADG1		; No.
	ADDI T3,1		; Bump the count
	STOR T3,GWICT,(GW)	; Store back
	ADDI T3,.GWILS-1	; Offset to first empty slot
	ADD T3,GW		; Where to store the address
	MOVEM T4,0(T3)		; Insert interface address into GW block
	SKIPN EOLFLG		; Read entire GW spec?
	 JRST LOADG1		; No.  Get another keyword/addr
	JRST LOADG6		; Yes.  Go tie off this block

; Process a keyword

LOADG4:	SETO T3,		; Keyword BAD flag
	CAIN T2,"P"		; "PRIME"
	 MOVX T3,GW%PRM
	CAIN T2,"D"		; "DUMB"
	 MOVX T3,GW%DUM
	CAIN T2,"H"		; "HOST"
	 MOVX T3,GW%HST
	CAIN T2,"A"
	 MOVX T3,GW%AUP		; "ALLWAYS-UP"
	HRROI T2,[ASCIZ "% INGGP: Unknown keyword "]
	JUMPL T3,LOADG9		; Give error if invalid keyword
	HRROI T2,[ASCIZ "% INGGP: Too many gateway type specs."]
	JN GWTYP,(GW),LOADG9	; Give error if already have spec
	STOR T3,GWTYP,(GW)	; Set type into GW block

; Here to skip over the rest of the current keyword

LOADG5:	CALL GETC		; Get a character
	JUMPE T2,LOADG8		; End of file?
	CAIN T2,12		; End of line?
	 JRST LOADG6		; Yes.  Go tie it off.
	CAIE T2," "		; Space
	CAIN T2,","		; Or comma will end it
	 JRST LOADG1		; Go read next keyword
	JRST LOADG5		; Keep reading the rest of this one

; Here to tie off the block which has been accumulating

LOADG6:	LOAD T2,GWICT,(GW)	; Get count of interfaces on gateway
	JUMPE T2,LOADG9		; None(?) forget this line
	XMOVEI T3,.GWILS(GW)	; Pointer to the interface list
LOAD60:	MOVE T4,0(T3)		; Get an interface address
;***** got to go
	LSH T4,-↑D24		; Extract net number
	CAIL T4,1		; Reasonable # ?
	 CAIL T4,MAXNET
	  JRST LOAD64		; No, skip that interface
	SKIPL LCLHID(T4)	; Can we talk to this interface?
	  JRST LOAD61		; Yes.  Keep this GW block
LOAD64:	ADDI T3,1		; Try next interface address
	SOJG T2,LOAD60		; Done all?
	JRST LOADG9		; Yes.  Flush unuseable GW

LOAD61:	SETONE <GWUP,GWHIS>,(GW)	; Initialize in the UP state
	MOVEI T1,WID(GWHIS)	; Number of bits of ping history
	STOR T1,GWSPC,(GW)	; Set the successful ping count to match
	MOVE T3,GWTAB		; Pointer to the table
	MOVEI T4,MAXGWA		; Size of it
LOAD62:	SKIPN 0(T3)		; Empty slot?
	 JRST LOAD69		; Yes.
	ADDI T3,1
	SOJG T4,LOAD62
	HRROI T2,[ASCIZ "% INGGP: Too many gateways "]
	JRST LOADG9

LOAD69:	MOVEM GW,0(T3)		; Insert in table
	SETZ T2,		; Give successful return
	JRST LOADGX

LOADG7:	SKIPA T2,[-1,,[ASCIZ "% INGGP: Bad format "]]
LOADG8:	HRROI T2,[ASCIZ "% INGGP: Premature end of file "]
LOADG9:	PUSH P,T2
	SKIPE T1,GW
	 CALL RETBLK
	POP P,T2

LOADGX:	MOVE T1,JFH
	POP P,GW
	RESTORE
	RET

; GETC(JFH)	Get a character from a file

;T1/	JFH of the file
;
;	CALL GETC
;Ret+1:	T1 preserved.  T2 has the chr or 0 if end of file


GETC:	BIN			; Read the file
	JUMPN T2,GETC2		; Jump if a character gotten
	GTSTS			; Read a null.
	TXNN T2,GS%EOF		; At end of file?
	 JRST GETC		; No.  Just flush the null
	MOVEI T2,0		; Set to return the EOF code
	JRST GETCX

GETC2:	CAIE T2,14		; Formfeed?
	CAIN T2,37		; TENEX EOL?
	 MOVEI T2,12		; Convert to linefeed
	CAIN T2,12		; Linefeed?
	 JRST GETCX		; Return that
	CAIGE T2," "		; Other control?
	 JRST GETC		; Yes.  Flush
	CAIL T2,"a"
	CAILE T2,"z"
	 CAIA			; Not lowercase
	 SUBI T2,"a"-"A"	; Raise lowercase
GETCX:	RET

;SNDGWD		Send our gateway file date to all

;(no args)
;
;	CALL SNDGWD
;Ret+1:	Always.

SNDGWD:	LOCAL <GWX>
	PUSH P,GW
	MOVSI GWX,-MAXGWA	; Set to scan GWTAB
SNDGW1:	HRRZ GW,GWX		; Get offset into table
	ADD GW,GWTAB		; Add in base of table
	SKIPN GW,0(GW)		; Get pointer to gateway block
	 JRST SNDGWX		; Nothing in this slot
	LOAD T1,GWTYP,(GW)	; Get the gateway type code
	CAIE T1,GW%PRM		; Prime?
	CAIN T1,GW%HST		; Host?
	 CALL SNDGFT		; Those guys might be interested
	AOBJN GWX,SNDGW1	; Loop over all gateways.
SNDGWX:	POP P,GW
	RESTORE
	RET

;SNDGFT(GW)	Send gateway file date and source to a gateway

;GW/	Pointer to gateway block
;
;	CALL SNDGFT
;Ret+1:	Always

SNDGFT:	PUSH P,PKT		; Protect globals
	PUSH P,GPKT

	MOVEI T1,↑D12		; Need 12 bytes of data space
	CALL FNDGWP		; Get gateway address & setup GGP packet
	JUMPE PKT,SNDGFX	; No address of no room
; T1/ GW adr, T2/ our adr on that net

	; Set up GGP part of packet

	MOVEI T1,GGP%FD		; File date code
	STOR T1,GPTYP,(GPKT)	; Store as GGP type
	MOVE T1,GFSRC		; Host which has the file
	STOR T1,GPSRC,(GPKT)	; Say we it can be had
	MOVE T2,GFTAD		; GTAD of creation
IFKA<	MOVEI T3,0(T2)		; Second within day
	DIVI T3,↑D<24*60*60>	; Compute fraction of a day
	LSH T3,-↑D17		; To right half
	HRR T2,T3		; Form TOPS20 type GTAD
>
	SUB T2,[DAY0]		; Make it fit in 32-bits easily
	LSH T2,-4
	STOR T2,GPTAD,(GPKT)
	CALL GGPCKS		; Compute it
	STOR T1,GPCKS,(GPKT)	; Insert in packet
	CALL SNDGAT		; Send it off
SNDGFX:	POP P,GPKT
	POP P,PKT
	RET

;SETROU		Set routing tables from GW status tables
;		Can be called just about anytime.  Usually,
;		tables are maintained incrementally, however.

;	CALL SETROU
;Ret+1:	Always.


SETROU:	LOCAL <IPTR>
	HRLZ IPTR,$NETS		; Set to scan interface table
SETRO1:	SKIPG T2,NETGWA(IPTR)	; Pick up a table pointer
	  JRST SETRO8
	MOVEI T1,MAXNET-1	; Count for BLT
	XMOVEI T3,1(T2)		; Dest for blt
	SETOM 0(T2)
	CALL XBLTA		; Do the appropriate BLT.
SETRO8:	AOBJN IPTR,SETRO1	; Do all interfaces
	CALL RSTDSP		; Reset gateway dispatches
	RESTORE
	RET

;RSTDSP		Reset gateway dispatches which are not set now

;
;	CALL RSTDSP
;Ret+1:	Always.  As many -1 (empty) marks removed as possible

RSTDSP:	LOCAL <GWX,IPTR,ICTR,GTAB>
	PUSH P,GW
	MOVSI GWX,-MAXGWA	; Set to scan GWTAB
RSTDS1:	HRRZ GW,GWX		; Get current offset in to table
	ADD GW,GWTAB		; Add in base of table
	MOVE GW,0(GW)		; Get pointer to gateway block
	JUMPE GW,RSTDS9		; Empty slot in table
	JE GWUP,(GW),RSTDS9	; Avoid using gateways which are down
	XMOVEI IPTR,.GWILS(GW)	; Pointer to interface list
	LOAD ICTR,GWICT,(GW)	; Get interface count

RSTDS2:	MOVE GTAB,0(IPTR)	; Get interface address
	LSH GTAB,-↑D24		; Net number of that interface
	SKIPG LCLHID(GTAB)	; One we are connected to?
	 JRST RSTDS8		; No.  Move on to next interface addr
	LOAD T1,INTNUM,LCLHID(GTAB) ; Get interface number
	MOVE GTAB,NETGWA(T1)	; Get pointer to dispatch table, that int.

	XMOVEI T3,.GWILS(GW)	; Set up to scan interface list again
	LOAD T4,GWICT,(GW)	; Putting exit points in this GWTAB
RSTDS3:	MOVE T2,0(T3)		; Get an interface address
	LSH T2,-↑D24		9 Get net number
	ADD T2,GTAB		; Corresponding entry in current table
	MOVE T1,0(IPTR)		; The address we are talking about
	SKIPGE 0(T2)		; Slot currently empty?
	 MOVEM T1,0(T2)		; Yes.  Stuff it.
	ADDI T3,1		; Move on to next interface address
	SOJG T4,RSTDS3		; Loop til all done.
RSTDS8:	ADDI IPTR,1		; Move to next interface
	SOJG ICTR,RSTDS2	; Loop til all done.
RSTDS9:	AOBJN GWX,RSTDS1	; Loop over all GWs

	MOVE T1,TODCLK		; Update time of
	ADD T1,GGPRT0		; next asynchronous
	MOVEM T1,GGPRTM		; call

	POP P,GW
	RESTORE
	RET

; Top level GGP Processing routine.  Called from main Internet
; fork loop.
; (no args)
;
;	CALL GGPPRC
;Ret+1:	Always


GGPPRC::SETZM GGPFLG		; Clear run request flag
	SKIPN GGPON
	  JRST GGPPRX

	CALL GGPDSP		; Dispatch any msgs which are waiting

	MOVE T1,GGPPTM		; Time of next PINGER
	CAMGE T1,TODCLK		; Over due?
	  CALL PINGER		; Yes.  Do ping stuff.

; Note:	PINGER may call RSTDSP which updates GGPRTM so it doesn't get recalled

	MOVE T1,GGPRTM		; Time of next RSTDSP
	CAMGE T1,TODCLK		; Over due?
	  CALL RSTDSP		; Yes. Reset dispatches

GGPPRX:	RET






;  Check Routine for GGP.  Tells when to run next

;T1/	A TODLCK
;
;	CALL GGPCHK
;Ret+1:	Always.  T1 has min of input T1 and when we should run next.

GGPCHK::MOVX T2,<377777777777>	; No run needed
	SKIPN GGPON
	  JRST GGPCH9

	CAMLE T2,GGPPTM		; Check against PINGER time
	  MOVE T2,GGPPTM		; That is sooner
	CAMLE T2,GGPRTM		; Check against RSTDSP time
	  MOVE T2,GGPRTM		; That is sooner

GGPCH9:	MOVEM T2,GGPTIM		; Time of next GGP run
	CAMLE T1,T2
	  MOVE T1,T2
	RET

; PINGER()	Ping gateways to see if they are alive

;
;	CALL PINGEN
;Ret+1:	Always.  GGPPTM reset for next run

PINGER:	LOCAL <I,REDOF>
	PUSH P,GW
	SETZ REDOF,		; Clear redo-routes flag
	MOVSI I,-MAXGWA		; Set to scan the gateway table
PINGE1:	HRRZ GW,I		; Get offset into table
	ADD GW,GWTAB		; Add base pointer
	SKIPN INTSCR		; No pings if secure
	SKIPN GW,0(GW)		; Get pointer to gateway
	 JRST PINGE8		; Unoccupied slot
	LOAD T1,GWHIS,(GW)	; Get the history bits
	LOAD T2,GWSPC,(GW)	; Get the successful ping count
	TRNE T1,1		; Test bit about to be forgotten
	 SUBI T2,1		; Forgotting a success
	SKIPGE T2		; Avoid negative while down
	 MOVEI T2,0		; This is as bad as you can get
	LOAD T3,GWPIP,(GW)	; See if previous ping still in progress
	XORI T3,1		; Flip sense to indicate success
	LSH T3,WID(GWHIS)-1	; Move to left end
	LSH T1,-1		; Flush the oldest history bit
	IOR T1,T3		; Include in history bits
	SKIPE T3		; Did we add a success to the list
	 ADDI T2,1		; Yes.  Count it up
	CAILE T2,WID(GWHIS)	; Check for overflow
	 MOVEI T2,WID(GWHIS)	; Limit to max
	STOR T2,GWSPC,(GW)	; Store back the count
	STOR T1,GWHIS,(GW)	; Store back the bits
	LOAD T4,GWUP,(GW)	; Get current state
	MOVE T3,T4		; Save a copy
	CAIL T2,.THRUP		; Enough success to say it's up?
	 MOVEI T4,1		; Yes
	CAIG T2,.THRDN		; So few that it is down?
	 MOVEI T4,0		; Yes.
	STOR T4,GWUP,(GW)	; Set new value

	XOR T3,T4		; Compare to see if change
	ADDM T3,REDOF		; If so, count up changes
	JUMPE T3,PINGE7		; Jump if no change
	JUMPN T4,PINGE7		; Jump if it came up
	CALL GWDOWN		; Yes.  Flush from tables now.
PINGE7:	SETONE GWPIP,(GW)	; Set ping-in-progress bit
	CALL SNDPNG		; Send a ping to guy in GW
PINGE8:	AOBJN I,PINGE1		; Loop over all gateways
	SKIPLE REDOF		; Any GW change state?
	 CALL RSTDSP		; Yes.  Reset dispatches

	MOVE T1,GGPPT0		; Interping interval
	ADD T1,TODCLK		; Time of next ping/check
	MOVEM T1,GGPPTM		; Save for scheduling
	POP P,GW
	RESTORE
	RET

;SNDPNG		Send a ping message to a GW

; This is an ECHO message if we are sending to a PRIME gateway, or
; an ECHO REPLY addressed to ourself if testing a non-PRIME
; gateway.  Net result is that we get back only ECHO REPLIES.


;GW/	Pointer to gateway block
;
;	CALL SNDPNG
;Ret+1:	Always.

SNDPNG:	PUSH P,PKT
	PUSH P,GPKT
	LOAD T1,GWTYP,(GW)	; Gateway type code
	CAIE T1,GW%AUP		; Always up?
	  JRST SNDPN1		; No.  Must actually send a ping
	SETZRO GWPIP,(GW)	; Fake a successful ping
	JRST SNDPNX

SNDPN1:	MOVEI T1,1		; GGP packet size
	CALL FNDGWP		; Get gateway address & setup GGP packet
	JUMPE PKT,SNDPNX	; No address of no room
; T1/ GW adr, T2/ our adr on that net
	MOVEI T3,GGP%EC		; Echo type
	LOAD T4,GWTYP,(GW)	; Get type
	CAIN T4,GW%PRM		; Prime?
	  JRST SNDPN6		; Yes, SRC & DST already set
	EXCH T1,T2		; Swap source and destination
	MOVEI T3,GGP%ER		; ECHO-REPLY code
	SETONE PNLCL,(PKT)	; No local delivery, send to "source"
	STOR T2,PISH,(PKT)	; Make it look like it came from there
	STOR T1,PIDH,(PKT)	; Make it go there
SNDPN6:	STOR T3,GPTYP,(GPKT)	; Set into GGP section
	CALL GGPCKS		; Compute checksum
	STOR T1,GPCKS,(GPKT)	; Insert in packet
	CALL SNDGAT		; Send it off
SNDPNX:	POP P,GPKT
	POP P,PKT
	RET

;GWDOWN		Gateway just detected down

;		Called by the PINGER when too few pings returned

;GW/	Pointer to gateway block
;
;	CALL GWDOWN
;Ret+1:	Always.  GWUP bit cleared and all interfaces removed from tables

GWDOWN:	LOCAL <IFX,NTX,GWX,GWSAV>
	MOVEM GW,GWSAV		; Save GW
	HRLZ IFX,$NETS		; Set to scan interface table
GWDOW1:	MOVSI NTX,-MAXNET	; Set to scan a table of interfaces
	SKIPG NETGWA(IFX)
	  JRST GWDOW9
GWDOW2:	HRRZ GWX,NTX		; Offset into current table
	ADD GWX,NETGWA(IFX)	; Which gateway address to consider
	SKIPGE T1,0(GWX)	; Get the address
	 JRST GWDOW8		; None there

	CALL FINDGW		; Find owning gateway
	JUMPE GW,GWDOW8		; Not there?

	CAMN GW,GWSAV		; Is on the gw which went down?
	 SETOM 0(GWX)		; Flush

GWDOW8:	AOBJN NTX,GWDOW2	; Scan entire dispatch table
GWDOW9:	AOBJN IFX,GWDOW1	; And all interfaces
	MOVE GW,GWSAV		; Restore the gateway pointer
	RESTORE
	RET

; GGPDSP	Dispatch on GGP message type

;
;	CALL GGPDSP
;Ret+1:	Always.


GGPDSP:	PUSH P,PKT
	PUSH P,GPKT
GGPDS1:	MOVE T1,GGPIPQ		; Pointer to input queue head
	LOAD PKT,QNEXT,(T1)	; Get first thing on queue
	SETSEC PKT,INTSEC	; Make extended address
	CAMN PKT,T1		; Pointer to head means empty
	 JRST GGPDSX		; Empty
	MOVE T1,PKT		; What to dequeue
	CALL DQ			; Get it off queue (NOSKED not needed)
	LOAD GPKT,PIDO,(PKT)	; Internet data offset
;	ADD GPKT,PKT		; Get pointer to GGP portion
	ADDI GPKT,PKTELI(PKT)	; Skip over local information

	CALL GGPCKS		; Check GGP Checksum
	JUMPE T1,GGPDS2		; Jump if ok

	MOVX T1,PT%GKC		; Trace packets w/ bad checksum
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKI		; Yes
	MOVX T1,IPP%PT		; Checksum parameter problem
	MOVX T2,12		; 12-13th octet are checksum
	CALL INCEM		; Report error

	AOS BADPCT		; Count bad packets
	JRST GGPDS9		; Flush it

GGPDS2:	MOVX T1,PT%GDI		; Trace packets received
	TDNE T1,INTTRC		; Want trace?
	  CALL PRNPKI		; Yes

	LOAD T1,GPTYP,(GPKT)	; What kind of message it is
	HRLZ T2,NGGPMT
	CAME T1,GGPTTB(T2)	; Matches this one?
	AOBJN T2,.-1		; No.  Try next.
	JUMPGE T2,[	MOVX T1,PT%GKT	; Jump if not found
			TDNE T1,INTTRC	; Want trace?
	  		  CALL PRNPKI	; Yes, Trace packets w/ unknown type
			MOVX T1,IPP%PT	; GPTYP parameter problem
			MOVE T2,GPKT
			SUBI T2,PKTELI(PKT)
			IMULI T2,4
		;	ADDI T2,0	; 0th octet in GGP part
			CALL INCEM	; Report error
			JRST GGPDS9]

	LOAD T3,PIPL,(PKT)	; Packet length in bytes
	LOAD T4,PIDO,(PKT)	; Internet data offset in words
	ASH T4,2		; Make that bytes
	SUB T3,T4		; Number of bytes in GGP part
	SUB T3,GGPMDC(T2)	; Minus min number req'd for this type
	JUMPL T3,[	MOVX T1,PT%GKS	; Jump if packet too short
			TDNE T1,INTTRC	; Want trace?
	  		  CALL PRNPKI	; Yes
			MOVX T1,IPP%PT	; PIPL parameter problem
			MOVX T2,2	; 2nd octet is PIPL
			CALL INCEM	; Report error
			JRST GGPDS9]
	CALL @GGPRTB(T2)	; Call action routine
GGPDS9:	  CALL RETPKT		; Return the packet to free storage
	JRST GGPDS1		; unless routine skips

GGPDSX:	POP P,GPKT
	POP P,PKT
	RET
; GGP Type code tables

.Z==20

GGPTTB:	BLOCK .Z	; Table of type codes (ordered by frequency)
GGPRTB:	BLOCK .Z	; Action routines
			; NB: they might skip return to retain PKT
GGPMDC:	BLOCK .Z	; Table of minimum data counts

DEFINE	GGPCM	(C<XX>,L<10000>)<
IFGE .Y-.Z,<IF1 <PRINTX % Too many GGP type codes for table>>
IFL  .Y-.Z,<
.X==.
	RELOC	GGPTTB+.Y
	GGP%'C
	RELOC	GGPRTB+.Y
	MSEC1,,.GGP'C
	RELOC	GGPMDC+.Y
	L
	RELOC	.X
.Y=.Y+1
	>
>

.Y==0
	GGPCM RU,8
	GGPCM AK,4
	GGPCM RD,8+MINIHS+8
	GGPCM SQ,4+MINIHS+8
	GGPCM DU,4+MINIHS+8
	GGPCM NK,4
	GGPCM ER,1
	GGPCM EC,1
	GGPCM HD,14
	GGPCM FD,14
	GGPCM NS,1

NGGPMT:	-<.Y>

IFG .Z-.Y,<REPEAT .Z-.Y,<	GGPCM>>
; Dummy message

GGP%XX==-1
.GGPXX:	RET



; Process an ECHO message:

.GGPEC:	LOAD T1,PIDH,(PKT)	; Destination (us)
	LOAD T2,PISH,(PKT)	; Source (who wants echo)
	STOR T1,PISH,(PKT)	; We are the echoer
	STOR T2,PIDH,(PKT)	; Sender is the echoee
	MOVEI T3,GGP%ER		; Echo reply code
	STOR T3,GPTYP,(GPKT)	; Set as the GGP type code
; Following doesn't work since RETPKT ignores PPROG, RETSKP instead
;	SETONE PPROG,(PKT)	; Save so it can be returned by caller
	CALL SNDGAT		; Send it back
	AOS (P)			; Skip return to save packet
	RET





; Process an ECHO-REPLY message:

.GGPER:	PUSH P,GW
	LOAD T1,PISH,(PKT)	; Who it appears to be from (maybe us)
	XMOVEI T2,..GPER	; Routine to call
	CALL FNDAGW		; Look up the gateway blocks
GGPERX:	POP P,GW
	RET

..GPER:	JUMPE GW,.GPERX		; Not there
	SETZRO GWPIP,(GW)	; Clear ping-in-progress bit
.GPERX:	RET

; Process a REDIRECT message

; Note: The current version of the GGP does not support hosts with
; multiple interfaces.  For example, a GGP%RD message assumes that we can
; look at the destination Host field of the packet which triggered the
; REDIRECT (header is in the REDIRECT msg), and tell which gateway we
; should stop sending to.  But we cannot tell if there are more than one
; interface.  The following code assumes that the tables have not changed
; (say by a background process doing load splitting) since the packet
; was sent.  Thus we can look at the destination net and find the right
; gateway table.



.GGPRD:	LOAD T1,PIDH,+GIP%RD-PKTELI(GPKT)	; Dest. which trigger lossage
				; [Maybe we should look at the local ldr]
	LSH T1,-↑D24		; Get net number
	CAIL T1,1
	CAILE T1,MAXNET		; Check number for valid
	 JRST GGPRDX		; What is that GW talking about????
	SKIPGE T4,LCLHID(T1)	; Are we connected to that net?
	 JRST GGPRDX		; No???

	LOAD T4,INTNUM,T4	; Interface number
	MOVE T4,NETGWA(T4)	; Pointer to gateway table
	ADD T1,T4		; Address of GW to dest net via this int.

	LOAD T2,GPGWA,(GPKT)	; Here is the replacement
	MOVEM T2,0(T1)		; Stuff in the table
GGPRDX:	RET

; Process an UNREACHABLE message

; This means that all paths into the indicated network are broken.
; This is because all the gateways talk to each other and agree about down
; nets.  So, all gateway dispatches can be wiped out.  Actually, we will
; leave dispatches through dumb gateways since they might still work.

; Every once in a while, a background process will call RSTDSP to re-init
; the tables.  This allows us to use nets which have come up again.

.GGPDU:	LOCAL <IPTR,NPTR>
	PUSH P,GW
	LOAD T1,GPCOD,(GPKT)	; Get code
	CAIE T1,GGP%NU		; Is it a net that is unreachable?
	 JRST GGPDUX		; Nope.  Can't handle others yet

	HRLZ IPTR,$NETS		; Set to scan all interfaces
GGPDU1:	SKIPG T3,NETGWA(IPTR)	; Get pointer to gateway table
	  JRST GGPDU9

; ***** will have to go  -↑D24
	LOAD T1,PIDHN,+GIP%DU-PKTELI(GPKT)	; Get destination net
	ADD T3,T1		; Point to slot for this net
	MOVE T1,0(T3)		; Get gateway address
	XMOVEI T2,..GPDU	; Routine to call
	CALL FNDAGW		; Set up GW to point at its block
				; and call ..GPDU
GGPDU9:	AOBJN IPTR,GGPDU1	; Loop over all interfaces
GGPDUX:	POP P,GW
	RESTORE
	RET



..GPDU:	JUMPE GW,..GPDX		; Not there.
	LOAD T4,GWTYP,(GW)	; Get the type code
	CAIE T4,GW%DUM		; Non-speaking GW?
	 SETOM 0(T3)		; No.  Wipe it out.
..GPDX:	RET



IFN %FULL,<
; Process a ROUTING-UPDATE message:

.GGPRU:	LOCAL <CNT,NETN,PTR>
	MOVEI NETN,1		; First net in packet is 1
	LOAD CNT,GPCNT,(GPKT)	; Get count from packet
	MOVE T1,.-1		; Get pointer to byte pointer
	MOVE PTR,0(T1)		; Get pointer to net list
GGPRU1:	JUMPLE CNT,GGPRU3	; Jump if all have been done
GGPRU2:	MOVE T1,NETN		; Current net number
	LSH T1,↑D24		; Make into a 32-bit style net number
	ILDB T2,PTR		; Get distance from this net
	CALL SETHGD		; Set the host group distance
	ADDI NETN,1		; Ready for next net
	SOJG CNT,GGPRU2		; Move on to next
GGPRU3:	CALL GGPACK		; Send an ACK
	RESTORE
	RET
> ; end IFN %FULL

IFN %FULL,<
; Process a HOST-DISTANCE message:

.GGPHD:	LOCAL <CNT,HDBLK>
	LOAD CNT,PIPL,(PKT)	; Get Internet packet length in bytes
	LOAD T2,PIDO,(PKT)	; Internet data offset in words
	ADDI T2,.GGPSO		; Add offset to first subblock
	ASH T2,2		; Make that bytes
	SUB CNT,T2		; Number of bytes of subblocks
	IDIVI CNT,4*HGSIZE	; Size of a subblock
	JUMPN CNT+1,GGPHDX	; Forget it if it has bad format
	XMOVEI HDBLK,.GGPSO(GPKT) ; Pointer to subblock

GGPHD1:	LOAD T1,HGHST,(HDBLK)	; Get 32-bit host group
	LOAD T2,HGDST,(HDBLK)	; Get distance thereto
	CALL SETHGD		; Set distance in our tables
	ADDI HDBLK,HGSIZE	; Move to next subblock
	SOJG CNT,GGPHD1		; Do it

	CALL GGPACK		; Generate an acknowledgement
GGPHDX:	RESTORE
	RET
> ; end IFN %FULL

; Process a FILE DATE message:

.GGPFD:	PUSH P,GW
	LOAD T1,GPSRC,(GPKT)	; Where the file is available
	LOAD T2,GPTAD,(GPKT)	; What its creation date is
	LOAD T3,PISH,(PKT)	; Who says so
	LSH T2,4		; Make in to our natural GTAD format
	ADD T2,[DAY0]
IFKA <	HRLZ T4,T2		; Get fraction of day
	LSH T4,-1		; Make arithmetic fraction
	MULI T4,↑D<24*60*60>	; Get second within day
	HRR T2,T4		; Form TENEX GTAD
>
	CAMN T2,GFTAD		; Compare with what we already know
	 JRST GGPFDX		; Equal means we are up-to-date here
	CAMG T2,GFTAD		; Packet announces something newer?
	 JRST GGPFD4		; No.  Tell him what we know

	MOVEM T2,GFTAD		; Yes.  Grab that info and tell operator
	MOVEM T1,GFSRC		; Save source
	MOVEM T3,GFWHO		; and who told us about the new info
	CALL ..GPFD		; Output message
	JRST GGPFDX

GGPFD4:	MOVE T1,GFWHO		; Who is giving us the old info
	CALL FINDGW		; Set up GW
	SKIPE GW		; Found it?
	 CALL SNDGFT		; Send him our more recent info

GGPFDX:	POP P,GW
	RET


..GPFD:	MOVE T1,GGPMSG
	HRROI T2,[ASCIZ "
% Internet address "]
	SETZ T3,
	SOUT
	MOVE T1,GGPMSG
	MOVE T2,GFWHO
	CALL PRNIH		; Print Internet Host Number or Name
	HRROI T2,[ASCIZ " says there is a new "]
	SETZ T3,
	SOUT
	HRROI T2,GWFILE
	SOUT
	HRROI T2,[ASCIZ " available from "]
	SOUT
	MOVE T2,GFSRC
	CALL PRNIH
	HRROI T2,[ASCIZ "
  Please get a copy, check its contents and install it.
"]
	SETZ T3,
	SOUT
	RET

; FINDGW	Set GW to point to (first) gateway block containing c(T1)

;T1/	An Internet Gateway address
;
;	CALL FINDGW
;Ret+1:	Always.  GW has pointer to block or 0 if not found


FINDGW:	LOCAL <GWX,ICT,ILS>
	MOVSI GWX,-MAXGWA	; Size of table
FINDG1:	HRRZ GW,GWX		; Get table offset
	ADD GW,GWTAB		; Add base
	SKIPN GW,0(GW)		; Get pointer to gateway block
	 JRST FINDG9		; Empty slot

	XMOVEI ILS,.GWILS(GW)	; Pointer to interface list
	LOAD ICT,GWICT,(GW)	; Number of interfaces
FINDG2:	CAMN T1,(ILS)		; Match?
	 JRST FINDGX		; Yes.  Get out.
	ADDI ILS,1		; Ready for next time
	SOJG ICT,FINDG2		; Try next interface

FINDG9:	AOBJN GWX,FINDG1	; Try next gateway
	MOVEI GW,0		; Indicate failure
FINDGX:	RESTORE
	RET

; FNDAGW	Set GW to point to each gateway block with address in T1
;		and call routine specified by T2

;T1/	An interface address
;T2/	Routine to call with GW set (must preserve all T* registers)
;	Cannot use caller's LOCALs
;
;	CALL FNDAGW
;Ret+1:	Always.


FNDAGW:	LOCAL <GWX,ICT,ILS>
	MOVSI GWX,-MAXGWA	; Size of table
FNDAG1:	HRRZ GW,GWX		; Get table offset
	ADD GW,GWTAB		; Add base
	SKIPN GW,0(GW)		; Get pointer to gateway block
	  JRST FNDAG9		; Empty slot

	XMOVEI ILS,.GWILS(GW)	; Pointer to interface list
	LOAD ICT,GWICT,(GW)	; Number of interfaces
FNDAG2:	CAMN T1,(ILS)		; Match?
	  CALL (T2)		; Yes.  Call routine.
	ADDI ILS,1		; Ready for next time
	SOJG ICT,FNDAG2		; Try next interface

FNDAG9:	AOBJN GWX,FNDAG1	; Try next gateway
FNDAGX:	RESTORE
	RET

; FNDPGW	Find a PRIME gateway

;T1/	Hint - desired network number
;
;	CALL FNDPGW
;Ret+1:	Always.  T1 has LOCAL address (or 0 if none) & T3 has interface #


FNDPGW::LOCAL <GWX,ICT,ILS>
	PUSH P,GW		; Save register
	MOVSI GWX,-MAXGWA	; Size of table
FNDPG1:	HRRZ GW,GWX		; Get table offset
	ADD GW,GWTAB		; Add base
	SKIPN GW,0(GW)		; Get pointer to gateway block
	  JRST FNDPG9		; Empty slot

	LOAD T3,GWTYP,(GW)	; Looking for PRIME
	CAIE T3,GW%PRM
	  JRST FNDPG9		; This isn't one
	JE GWUP,(GW),FNDPG9	; Must be up

	XMOVEI ILS,.GWILS(GW)	; Pointer to interface list
	LOAD ICT,GWICT,(GW)	; Number of interfaces
FNDPG2:	MOVE T3,(ILS)		; Get one of its addresses
; *****
	LSH T3,-↑D24		; Find net #
	CAIL T3,1
	 CAIL T3,MAXNET
	  JRST FNDPG4		; ??
	MOVE T3,LCLHID(T3)	; Get address & interface #
	JUMPLE T3,FNDPG4	; Not a LOCAL net
	
	MOVE T1,T3		; Extract local net address
	TLZ T1,740000		; Just 32 bits
	LOAD T3,INTNUM,(T3)	; Interface # too
	JRST FNDPGX		; All set

FNDPG4:	ADDI ILS,1		; Ready for next time
	SOJG ICT,FNDPG2		; Try next interface

FNDPG9:	AOBJN GWX,FNDPG1	; Try next gateway
	  MOVEI T1,0		; Indicate failure
FNDPGX:	POP P,GW		; Restore reg
	RESTORE
	RET

; FNDGWP	Find address of a gateway

; T1/	# octets in GGP packet to be sent
; GW/	(Extended) address of a gateway block
;
;	CALL FNDGWP
;	JUMPE PKT,error
;Ret+1:	Always, PKT=0 if error
; if no error
; PKT & GPKT set
; T1/	Addresss of gateway
; T2/	Our (primary) address

FNDGWP:	LOCAL <GGPSZB,GWADR,OURADR>
	MOVEM T1,GGPSZB

	XMOVEI T3,.GWILS(GW)	; Pointer to interface list
	LOAD T4,GWICT,(GW)	; Count of interfaces
FNDGWR:	MOVE GWADR,0(T3)	; Get an interface address from this GW
	MOVE T2,GWADR		; Copy it
	LSH T2,-↑D24		; Extract network number
	ADDI T3,1		; Advance to next address in list
	CAIL T2,1		; Reasonable net # ?
	 CAIL T2,MAXNET
	  JRST FNDGWT		; No
	SKIPL OURADR,LCLHID(T2)	; Can we talk to this interface?
	  JRST FNDGWV		; Yes.  Go do it.
FNDGWT:	SOJG T4,FNDGWR		; Loop over all
	SETZ PKT,		; Error return
	JRST FNDGWX		; Not crucial if we cannot find it

FNDGWV:	MOVE T1,GGPSZB
	CALL GGPIPK		; Initialize packet
	JUMPE PKT,FNDGWX	; No space
	MOVE T2,OURADR		; Our Internet name on that net
	TLZ T2,740000		; Clean it up
	STOR T2,PISH,(PKT)	; That's who it is from
	MOVE T1,GWADR		; Gateway Address
	STOR T1,PIDH,(PKT)	; This is the destination
FNDGWX:	RESTORE
	RET

; GGPIPK	Get & Initialize a GGP packet

;T1/	# octets of GGP packet
;
;	CALL GGPIPK
;Ret+1:	Always. PKT=0 if no memory error; PKT & GPKT set if ok


GGPIPK:	LOCAL <GGPSZB,GGPSZW>
	MOVEI GGPSZB,MINIHS(T1)	; Save (min IP)+GGP size, octets
	ADDI T1,3
	ASH T1,-2		; GGP words
	ADDI T1,PKTELI+.RTJST(-1,PIDO) ; Plus local & net & IP
	MOVEM T1,GGPSZW		; Save packet size, words

	CALL GETBLK		; Get storage in which to build pkt
	SKIPN PKT,T1		; Put in standard place
	  JRST GGPIPX		; Lose, PKT is 0

	MOVE T2,GGPSZW		; Size again
	CALL CLRBLK		; Clear all flags, checksum, etc

	MOVEI T1,.INTVR		; Internet Protocol version
	STOR T1,PIVER,(PKT)
	MOVEI GPKT,<MINIHS+3>/4	; Min. Internet header size
	STOR GPKT,PIDO,(PKT)	; Set as Internet Data offset
	ADDI GPKT,PKTELI	; Skip over local
	ADD GPKT,PKT		; & IP sections
	STOR GGPSZB,PIPL,(PKT)
	AOS T1,GGPSID		; Get next segment ID
	STOR T1,PISID,(PKT)	; Put in packet
	MOVEI T1,2		; GGP Time to live
	STOR T1,PITTL,(PKT)	; A local net requires 1 + 1 extra
	MOVEI T1,.GGPFM		; Protocol is Gateway-to-Gateway
	STOR T1,PIPRO,(PKT)

GGPIPX:	RESTORE			; Failure return
	RET

;PRNIH(32-bit number)	Print Internet Host Number/String

;T1/	JFH
;T2/	The 32-bit number
;
;	CALL PRNIH
;Ret+1:	Always.

PRNIH:	MOVE T4,T2		; Save a copy
	MOVEI T3,↑D10		; Decimal seems to be standard these days
	LDB T2,[POINT 8,T4,11]	; Net byte
	NOUT
	 JFCL
	MOVEI T2," "
	BOUT
	LDB T2,[POINT 8,T4,19]	; Host byte
	NOUT
	 JFCL
	MOVEI T2," "
	BOUT
	LDB T2,[POINT 8,T4,27]	; Logical host byte
	NOUT
	 JFCL
	MOVEI T2," "
	BOUT
	LDB T2,[POINT 8,T4,35]	; IMP byte
	NOUT
	 JFCL
	RET

; GGPCKS	Compute GGP level checksum

;PKT/	Pointer to Internet portion of packet
;GPKT/	Pointer to GGP section thereof
;
;	CALL GGPCKS
;Ret+1:	Always.  T1 has checksum.

GGPCKS:	MOVEI T1,0			; Not defined yet
	RET

; Things not yet done:

IFE %FULL,<
.GGPHD:
.GGPRU:
> ; end IFE %FULL
.GGPAK:
.GGPSQ:
.GGPNK:
.GGPNS:
GGPACK:

SETHGD:
	RET


	TNXEND
	END